/*
 * @Author: tyz
 * @Date: 2021-06-24 08:59:39
 * @LastEditTime: 2021-06-24 09:28:50
 * @Description: 自定义路由守卫
 * @FilePath: \nest_demo\src\tools\guard\roles.guard.ts
 */
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

/*
    * 思路：
    *   1. 从nest公共库中引入'CanActivate','ExecutionContext','Injectable'
    *   2. 从nest核心库中引入'Reflector'
    *   3. 自定义RolesGuard类实现CanActivate接口，并重写内部方法
    *   4. 使用的时候，只需要引入此自定义路由守卫并当成装饰器装饰所需鉴权的路由即可
    * 
    * 为什么引入'Injectable'?（重点）
    *   1. 因为需要注入到nest框架中才能使用，凡是需要注入到nest框架中的方法类都必须使用'@Injectable()'装饰
    * 
    * 为什么引入'CanActivate'?（重点）
    *   1. 自定义路由守卫必须实现nest框架提供的'CanActivate'接口，并一同重写其内部方法
    * 
    * 为什么引入'ExecutionContext'?
    *   1. canActivate()方法接受的参数必须是'ExecutionContext'数据类型
    *   2. 自定义路由最终会当做装饰器使用，在controller控制器中装饰需要进行权限控制的路由接口
    *   3. 这样才能拿到所进行装饰的路由内部各种参数，比如请求数据等等
    * 
    * 为什么引入'Reflector'?
    *   1. 获取我们自定义的装饰器参数
    *   2. 通过判断装饰器参数与请求数据传递的权限识别码是否匹配来判断当前角色是否具备权限并访问该接口
*/ 


@Injectable()
export class RolesGuard implements CanActivate{
    constructor(private readonly reflector:Reflector){}

    canActivate(context:ExecutionContext):boolean{
        // 获取字段为'roles'的装饰器
        const roles = this.reflector.get<string[]>('roles', context.getHandler());

        // 判断'roles'装饰器是否存在，不存在默认为true并允许访问
        if(!roles){
            return true;
        };

        // 否则，通过'ExecutionContext'数据类型的context获取路由请求信息
        const request = context.switchToHttp().getRequest();
        // 解析路由请求信息中的请求参数
        const { userAuth } = request.query;

        /*
            * 1. 判断存在'roles'装饰器存在的情况下，装饰器参数与路由请求参数中的权限字段是否匹配
            * 2. 匹配true - 允许访问接口， 不匹配false - 禁止访问接口并报错
        */
        return !!roles.find(role => role === userAuth);
    }
};